package cn.iocoder.yudao.module.infra.service.file;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.yudao.module.infra.enums.file.FileTypeEnums.*;

/**
 * 文件 Service 实现类
 *
 * @author 芋道源码
 */
@Service
public class FileServiceImpl implements FileService {

    @Resource
    private FileConfigService fileConfigService;

    @Resource
    private FileMapper fileMapper;

    @Override
    public PageResult<FileDO> getFilePage(FilePageReqVO pageReqVO) {
        return fileMapper.selectPage(pageReqVO);
    }

    @Value("${ny.profile}")
    private String profile;

    @Override
    @SneakyThrows
    public String createFile(String name, String path, byte[] content) {
        // 计算默认的 path 名
        String type = FileTypeUtils.getMineType(content, name);
        if (StrUtil.isEmpty(path)) {
            path = FileUtils.generatePath(content, name);
        }
        // 如果 name 为空，则使用 path 填充
        if (StrUtil.isEmpty(name)) {
            name = path;
        }

        // 上传到文件存储器
        FileClient client = fileConfigService.getMasterFileClient();
        Assert.notNull(client, "客户端(master) 不能为空");
        String url = client.upload(content, path, type);

        // 保存到数据库
        FileDO file = new FileDO();
        file.setConfigId(client.getId());
        file.setName(name);
        file.setPath(path);
        file.setUrl(url);
        file.setType(type);
        file.setSize(content.length);
        fileMapper.insert(file);
        return url;
    }

    @Override
    public Long createFile(FileCreateReqVO createReqVO) {
        FileDO file = BeanUtils.toBean(createReqVO, FileDO.class);
        fileMapper.insert(file);
        return file.getId();
    }

    @Override
    public void deleteFile(Long id) throws Exception {
        // 校验存在
        FileDO file = validateFileExists(id);

        // 从文件存储器中删除
        FileClient client = fileConfigService.getFileClient(file.getConfigId());
        Assert.notNull(client, "客户端({}) 不能为空", file.getConfigId());
        client.delete(file.getPath());

        // 删除记录
        fileMapper.deleteById(id);
    }

    private FileDO validateFileExists(Long id) {
        FileDO fileDO = fileMapper.selectById(id);
        if (fileDO == null) {
            throw exception(FILE_NOT_EXISTS);
        }
        return fileDO;
    }

    @Override
    public byte[] getFileContent(Long configId, String path) throws Exception {
        FileClient client = fileConfigService.getFileClient(configId);
        Assert.notNull(client, "客户端({}) 不能为空", configId);
        return client.getContent(path);
    }

    @Override
    public FilePresignedUrlRespVO getFilePresignedUrl(String path) throws Exception {
        FileClient fileClient = fileConfigService.getMasterFileClient();
        FilePresignedUrlRespDTO presignedObjectUrl = fileClient.getPresignedObjectUrl(path);
        return BeanUtils.toBean(presignedObjectUrl, FilePresignedUrlRespVO.class,
                object -> object.setConfigId(fileClient.getId()));
    }

    @Override
    public void deleteFileByUrl(String url) throws Exception {
        FileDO file = validateFileExistsByUrl(url);
        FileClient client = fileConfigService.getFileClient(file.getConfigId());
        Assert.notNull(client, "客户端({}) 不能为空", file.getConfigId());
        client.delete(file.getPath());
        fileMapper.deleteById(file.getId());
    }

    @Transactional
    public List<String> uploadImage(List<MultipartFile> files) throws IOException {
        List<String> filePaths = new ArrayList<>();
        LocalDate currentDate = LocalDate.now();
        String year = String.valueOf(currentDate.getYear());
        String month = String.format("%02d", currentDate.getMonthValue());
        String day = String.format("%02d", currentDate.getDayOfMonth());
        String datePath = year + "/" + month + "/" + day + "/";

        // 确保 profile 以斜杠结尾
        if (!profile.endsWith("/")) {
            profile += "/";
        }

        // 完整的本地目录
        String localDirectory = profile + datePath;
        Path directoryPath = Paths.get(localDirectory);

        // 创建目录
        if (!Files.exists(directoryPath)) {
            Files.createDirectories(directoryPath);
        }

        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                throw new RuntimeException("图片不允许为空");
            }
            // 检查文件类型
            String contentType = file.getContentType();
            if (!isImage(contentType)) {
                throw new RuntimeException("只允许上传图片文件");
            }

            String originalFilename = file.getOriginalFilename();
            String randomFileName = UUID.randomUUID().toString().replace("-", "") + "_" + originalFilename;
            Path filePath = directoryPath.resolve(randomFileName);
            Files.write(filePath, file.getBytes());

            // 只回显相对路径
            filePaths.add(datePath + randomFileName);
        }
        return filePaths;
    }


    @Override
    public Map<String, String> uploadPrvateKey(MultipartFile file, String appId) throws IOException {
        LocalDate currentDate = LocalDate.now();
        String year = String.valueOf(currentDate.getYear());
        String month = String.format("%02d", currentDate.getMonthValue());
        String day = String.format("%02d", currentDate.getDayOfMonth());
        String datePath = "key/" + year + "/" + month + "/" + day + "/";

        Map<String, String> fileInfoMap = new HashMap<>();
        // 确保 profile 以斜杠结尾
        if (!profile.endsWith("/")) {
            profile += "/";
        }
        // 完整的本地目录
        String localDirectory = profile + datePath;
        Path directoryPath = Paths.get(localDirectory);
        // 创建目录
        if (!Files.exists(directoryPath)) {
            Files.createDirectories(directoryPath);
        }

        if (file.isEmpty()) {
            throw new RuntimeException("privateKey文件不允许为空");
        }
        // 检查文件类型
        String contentType = file.getContentType();
        if (!isPemFile(contentType)) {
            throw new RuntimeException("文件上传格式不正确,请检查");
        }
        String originalFilename = file.getOriginalFilename();
        String randomFileName = appId + "_" + originalFilename;
        Path filePath = directoryPath.resolve(randomFileName);
        Files.write(filePath, file.getBytes());

        // 读取文件内容
        StringBuilder pemContent = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(filePath)))) {
            String line;
            while ((line = br.readLine()) != null) {
                pemContent.append(line).append("\n");
            }
        } catch (IOException e) {
            throw new IOException("Error reading file: " + filePath.toString(), e);
        }

        // 移除文件内容中的头尾标记
        String pemString = pemContent.toString();
//                .replace("-----BEGIN PRIVATE KEY-----\n", "")
//                .replace("\n-----END PRIVATE KEY-----\n", "");

        // 将文件路径（相对路径）和内容添加到map中
        fileInfoMap.put("url", datePath + randomFileName);
        fileInfoMap.put("content", pemString);

        return fileInfoMap;
    }


    private FileDO validateFileExistsByUrl(String url) {
        FileDO fileDO = fileMapper.selectByUrl(url);
        if (fileDO == null) {
            throw exception(FILE_NOT_EXISTS);
        }
        return fileDO;
    }

}
